package com.lct.simpleforecast.ui.custom

import android.animation.Animator
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import com.lct.simpleforecast.R
import org.jetbrains.anko.dip
import org.jetbrains.anko.sp

/**
 * Author：SkySmile
 * Date：2018/7/6
 * Description：页面加载数据时的loading，默认是空白页面。
 * 加载中时 loading在左，文本在右，没有文本的话 loading默认居中
 * 加载失败时，图片在上，文本下。没有图片时，文本默认居中。没有文本时图片默认居中
 */
class LoadingView : View {
    //加载文本，默认为空
    var loadingText: String? = null
    //加载失败文本，默认为空
    var loadingFailText: String? = null
    //加载文本颜色
    var loadingTextColor: Int = 0
    //加载失败文本颜色
    var loadingFailTextColor: Int = 0
    //加载文本大小
    var loadingTextSize: Int = 0
    //加载失败文本大小
    var loadingFailTextSize: Int = 0
    //加载失败显示的图片
    var loadingFailImage: Bitmap? = null
    //图片的绘制范围
    private val mImageBound = Rect()
    //文本占用空间范围（矩形）
    private val mTextBound = Rect()
    //第一个圆环的颜色
    var loadingFirstColor: Int = 0
    //第二个圆环的颜色
    var loadingSecondColor: Int = 0
    //圆环的半径
    var loadingRingRadius: Int = 0
    //圆环的宽度
    var loadingRingWidth: Int = 0
    //进度更新的速度,值越小，速度越快
    var loadingSpeed: Int = 0
    //显示的位置
    var loadingAlign: Int = 0
    //用于定义圆弧的形状和大小的界限
    private val oval: RectF = RectF(0f, 0f, 0f, 0f)
    //画笔
    private val mPaint = Paint()
    //圆心的x坐标
    private var centreX: Float = 0f
    //圆心的y坐标
    private var centreY: Float = 0f
    //圆的半径
    private var radius: Float = 0f
    //进度
    private var progress: Int = 0
    //颜色切换的判断条件
    private var isChange: Boolean = true
    //圆和加载文本之间的间隔 20px
    private val intervalLoading = 20
    //图片和加载失败文本之间的间隔 20px
    private val intervalLoadingFail = 20
    //更近进度的属性动画
    private val animator = ObjectAnimator.ofInt(0, 360)
    //动画是否执行完一次
    private var isRepeatFinish = false
    //加载中
    private var isLoading = false
    //加载失败
    private var isLoadingFail = false
    //显示在中间
    private var isLoadingAtCenter = false
    //显示在顶部
    private var isLoadingAtTop = false
    //点击事件
    private lateinit var doOnClickAction: () -> Unit

    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
            : super(context, attrs, defStyleAttr) {
        //获取自定义的属性值
        getAttrs(context, attrs, defStyleAttr)
        //初始化动画
        initAnimator()
        //设置点击事件
        this.setOnClickListener { if (isLoadingFail) doOnClickAction() }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        animator.cancel()
    }

    /**
     * 获取自定义属性的值
     */
    private fun getAttrs(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
        val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.LoadingView,
                defStyleAttr, 0)
        loadingText = typedArray.getString(R.styleable.LoadingView_loadingText)
        loadingFailText = typedArray.getString(R.styleable.LoadingView_loadingFailText)
        //默认灰色
        loadingTextColor = typedArray.getColor(R.styleable.LoadingView_loadingTextColor, Color.GRAY)
        //默认灰色
        loadingFailTextColor = typedArray.getColor(R.styleable.LoadingView_loadingFailTextColor,
                Color.GRAY)
        //默认16sp
        loadingTextSize = typedArray.getDimensionPixelSize(R.styleable.LoadingView_loadingTextSize,
                sp(16))
        //默认16sp
        loadingFailTextSize = typedArray.getDimensionPixelSize(
                R.styleable.LoadingView_loadingFailTextSize, sp(16))
        //默认0，表示没有设置加载失败时的图片
        loadingFailImage = BitmapFactory.decodeResource(resources, typedArray.getResourceId(
                R.styleable.LoadingView_loadingFailImage, 0))
        //默认白色
        loadingFirstColor = typedArray.getColor(R.styleable.LoadingView_loadingFirstColor,
                Color.WHITE)
        //默认绿色
        loadingSecondColor = typedArray.getColor(R.styleable.LoadingView_loadingSecondColor,
                Color.GREEN)
        //默认15dp
        loadingRingRadius = typedArray.getDimensionPixelSize(
                R.styleable.LoadingView_loadingRingRadius, dip(15))
        //默认3dp
        loadingRingWidth = typedArray.getDimensionPixelSize(R.styleable.LoadingView_loadingRingWidth,
                dip(3))
        //默认10
        loadingSpeed = typedArray.getInt(R.styleable.LoadingView_loadingSpeed, 10)
        //显示的位置，默认显示在中间
        loadingAlign = typedArray.getInt(R.styleable.LoadingView_loadingAlign, 0)
        if (loadingAlign == 0) {
            isLoadingAtCenter = true
            isLoadingAtTop = false
        } else if (loadingAlign == 2) {
            isLoadingAtCenter = false
            isLoadingAtTop = true
        }
        typedArray.recycle()
    }

    /**
     * 初始化属性动画
     */
    private fun initAnimator() {
        var temp = 0
        animator.addUpdateListener {
            //这里progress，不是每次递增1，所有做下处理
            progress = it.animatedValue as Int
            if (progress != temp) {
                temp = progress
                if (progress >= 359 && isRepeatFinish) {
                    isRepeatFinish = false
                    temp = 0
                    progress = 360
                }
                postInvalidate()
            }
        }
        animator.addListener(object : Animator.AnimatorListener {

            override fun onAnimationRepeat(animation: Animator?) {
                isRepeatFinish = true
                isChange = !isChange
            }

            override fun onAnimationEnd(animation: Animator?) {

            }

            override fun onAnimationCancel(animation: Animator?) {

            }

            override fun onAnimationStart(animation: Animator?) {
            }
        })
        animator.duration = (loadingSpeed * 100).toLong()
        //执行次数：无线循环
        animator.repeatCount = ValueAnimator.INFINITE
        //插值器：先加速后减速
        animator.interpolator = AccelerateDecelerateInterpolator()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        //计算宽度
        val width = if (widthMode == MeasureSpec.EXACTLY) widthSize else measureWidth()
        //计算高度
        val height = if (heightMode == MeasureSpec.EXACTLY) heightSize else measureHeight()
        //交给系统测量
        setMeasuredDimension(width, height)
    }

    /**
     * 测量高度
     */
    private fun measureHeight(): Int {
        var height = 0
        if (isLoading) {
            //加载中，上下padding + (圆的直径 和 文本的高度 中的最大值)
            loadingText?.let {
                mPaint.textSize = loadingTextSize.toFloat()
                mPaint.getTextBounds(it, 0, it.length, mTextBound)
                height = paddingTop +
                        Math.max((loadingRingRadius - loadingRingWidth / 2) * 2, mTextBound.height()) +
                        paddingBottom
            }
            if (loadingText == null) {
                height = paddingTop + (loadingRingRadius - loadingRingWidth / 2) * 2 + paddingBottom
            }
        } else if (isLoadingFail) {
            //加载失败，上下padding + 图片的高度 + 图片和文本的间隔 + 文本的高度
            loadingFailImage?.let {
                val imageHeight = it.height
                loadingFailText?.let {
                    mPaint.textSize = loadingFailTextSize.toFloat()
                    mPaint.getTextBounds(it, 0, it.length, mTextBound)
                    height = paddingTop + imageHeight + intervalLoadingFail + mTextBound.height() +
                            paddingBottom
                }
                if (loadingFailText == null) {
                    height = paddingTop + imageHeight + paddingBottom
                }
            }
            if (loadingFailImage == null) {
                loadingFailText?.let {
                    mPaint.textSize = loadingFailTextSize.toFloat()
                    mPaint.getTextBounds(it, 0, it.length, mTextBound)
                    height = paddingTop + mTextBound.height() +
                            paddingBottom
                }
            }
        }
        return height
    }

    /**
     * 测量宽度
     */
    private fun measureWidth(): Int {
        var width = 0
        if (isLoading) {
            //加载中，width = 左右padding + 圆的直径 + 圆和文本的间隔 + 文本的宽度
            loadingText?.let {
                mPaint.textSize = loadingTextSize.toFloat()
                mPaint.getTextBounds(it, 0, it.length, mTextBound)
                width = paddingLeft +
                        (loadingRingRadius - loadingRingWidth / 2) * 2 +
                        intervalLoading +
                        mTextBound.width() +
                        paddingRight
            }
            if (loadingText == null) {
                width = paddingLeft + (loadingRingRadius - loadingRingWidth / 2) * 2 + paddingRight
            }
        } else if (isLoadingFail) {
            //加载失败，width = 左右padding + (图片的宽度 和 文本的宽度 中的最大值)
            loadingFailImage?.let {
                val imageWidth = it.width
                loadingFailText?.let {
                    mPaint.textSize = loadingFailTextSize.toFloat()
                    mPaint.getTextBounds(loadingFailText, 0, it.length, mTextBound)
                    width = paddingLeft + Math.max(imageWidth, mTextBound.width()) + paddingRight
                }
                if (loadingFailText == null) {
                    width = paddingLeft + imageWidth + paddingRight
                }
            }
            if (loadingFailImage == null) {
                loadingFailText?.let {
                    mPaint.textSize = loadingFailTextSize.toFloat()
                    mPaint.getTextBounds(loadingFailText, 0, it.length, mTextBound)
                    width = paddingLeft + mTextBound.width() + paddingRight
                }
            }
        }
        return width
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (isLoading) {
            drawLoadingContent(canvas)
        }
        if (isLoadingFail) {
            drawLoadingFailContent(canvas)
        }
    }

    private fun drawLoadingContent(canvas: Canvas) {
        //画圆
        drawCircle(canvas)
        //绘制加载文本
        drawText(canvas)
    }

    private fun drawLoadingFailContent(canvas: Canvas) {
        //绘制图片
        drawImage(canvas)
        //绘制加载失败时的文本
        drawFailText(canvas)
    }

    private fun drawCircle(canvas: Canvas) {
        //半径
        radius = (loadingRingRadius - loadingRingWidth / 2).toFloat()
        //圆心的x坐标
        centreX = calculateXY("x")
        //圆心的y坐标
        centreY = calculateXY("y")
        //定义圆弧的形状和大小的界限
        oval.left = centreX - radius
        oval.top = centreY - radius
        oval.right = centreX + radius
        oval.bottom = centreY + radius
        //设置圆环的宽度
        mPaint.strokeWidth = loadingRingWidth.toFloat()
        //消除锯齿
        mPaint.isAntiAlias = true
        //设置空心
        mPaint.style = Paint.Style.STROKE
        //第一圈的颜色画完之后，开始画第二圈的颜色
        if (isChange) {
            drawLoadingCircle(canvas, loadingFirstColor, loadingSecondColor)
        } else {
            drawLoadingCircle(canvas, loadingSecondColor, loadingFirstColor)
        }
    }

    private fun drawText(canvas: Canvas) {
        loadingText?.let {
            mPaint.color = loadingTextColor
            mPaint.textSize = loadingTextSize.toFloat()
            mPaint.isAntiAlias = true
            mPaint.strokeWidth = 0f
            mPaint.getTextBounds(it, 0, it.length, mTextBound)
            //(x,y) 坐标点是左下角，把文本绘制在圆的右边并且垂直居中
            val x = centreX + radius + intervalLoading
            var y = 0f
            if (isLoadingAtCenter) {
                y = (height / 2 + mTextBound.height() / 2).toFloat()
            } else if (isLoadingAtTop) {
                //圆的直径大于文本的高度，文本绘制在圆的中间，否则直接绘制
                y = if (radius * 2 > mTextBound.height())
                    (radius * 2 + mTextBound.height()) / 2 + paddingTop
                else
                    (mTextBound.height() + paddingTop).toFloat()
            }
            canvas.drawText(loadingText, x, y, mPaint)
        }
    }

    /**
     * 计算圆心的坐标，使圆心和文本位于屏幕中心
     *
     * @param tag x | y
     * @return x | y
     */
    private fun calculateXY(tag: String): Float {
        var coordinate = 0f
        if (tag == "x") {
            //x轴
            coordinate = (width / 2).toFloat()
            loadingText?.let {
                mPaint.textSize = loadingTextSize.toFloat()
                mPaint.getTextBounds(it, 0, it.length, mTextBound)
                //圆心的x坐标：(屏幕宽度 - (圆的直径 + 圆和文本之间的间隔（10px）+ 文本的宽度))
                // 除以 2 + 半径
                coordinate = (width - (radius * 2 + intervalLoading + mTextBound.width())) / 2 +
                        radius
            }
        } else {
            //y轴
            if (isLoadingAtCenter) {
                coordinate = (height / 2).toFloat()
            } else if (isLoadingAtTop) {
                coordinate = radius + paddingTop
            }
        }
        return coordinate
    }


    /**
     * 画圆和圆弧
     */
    private fun drawLoadingCircle(canvas: Canvas, color1: Int, color2: Int) {
        //设置圆的颜色
        mPaint.color = color1
        //画出圆
        canvas.drawCircle(centreX, centreY, radius, mPaint)
        //设置圆弧的颜色
        mPaint.color = color2
        //根据进度画圆弧
        canvas.drawArc(oval, -90f, progress.toFloat(), false, mPaint)
    }

    /**
     * 画加载失败时的图片
     */
    private fun drawImage(canvas: Canvas) {
        loadingFailImage?.let {
            val imageHeight = it.height
            var left = 0
            var right = 0
            var top = 0
            var bottom = 0
            if (isLoadingAtCenter) {
                left = width / 2 - it.width / 2
                right = width / 2 + it.width / 2
                top = height / 2 - imageHeight / 2
                bottom = height / 2 + imageHeight / 2
            } else if (isLoadingAtTop) {
                left = width / 2 - it.width / 2
                right = width / 2 + it.width / 2
                top = paddingTop
                bottom = imageHeight + paddingTop
            }
            loadingFailText?.let {
                mPaint.textSize = loadingFailTextSize.toFloat()
                mPaint.getTextBounds(it, 0, it.length, mTextBound)
                if (isLoadingAtCenter) {
                    top = height / 2 - (imageHeight + intervalLoadingFail + mTextBound.height()) / 2
                    bottom = height / 2 + imageHeight / 2 -
                            (intervalLoadingFail + mTextBound.height()) / 2
                } else if (isLoadingAtTop) {
                    top = paddingTop
                    bottom = imageHeight + paddingTop
                }
            }
            mImageBound.left = left
            mImageBound.right = right
            mImageBound.top = top
            mImageBound.bottom = bottom
            canvas.drawBitmap(it, null, mImageBound, mPaint)
        }
    }

    private fun drawFailText(canvas: Canvas) {
        loadingFailText?.let {
            mPaint.textSize = loadingFailTextSize.toFloat()
            mPaint.color = loadingFailTextColor
            mPaint.isAntiAlias = true
            mPaint.getTextBounds(it, 0, it.length, mTextBound)
            var x = 0f
            var y = 0f
            if (isLoadingAtCenter) {
                x = (width / 2 - mTextBound.width() / 2).toFloat()
                y = (height / 2 + mTextBound.height() / 2).toFloat()
                loadingFailImage?.let {
                    y = (height / 2 + (it.height + intervalLoadingFail + mTextBound.height()) / 2)
                            .toFloat()
                }
            } else if (isLoadingAtTop) {
                x = (width / 2 - mTextBound.width() / 2).toFloat()
                y = (mTextBound.height() + paddingTop).toFloat()
                loadingFailImage?.let {
                    y = (it.height + intervalLoadingFail + mTextBound.height() + paddingTop).toFloat()
                }
            }
            canvas.drawText(loadingFailText, x, y, mPaint)
        }
    }

    /**
     * 显示加载中loading
     */
    fun showLoading() {
        if (!isLoading) {
            isLoading = true
            isLoadingFail = false
            isRepeatFinish = false
            isChange = true
            this.visibility = View.VISIBLE
            animator.start()
        }
    }

    /**
     * 隐藏加载loading
     */
    fun hideLoading() {
        if (isLoading) {
            isLoading = false
            animator.cancel()
            this.visibility = View.GONE
        }
    }

    /**
     * 显示加载失败loading
     */
    fun showLoadingFail(doOnClick: () -> Unit = {}) {
        this.doOnClickAction = doOnClick
        if (!isLoadingFail) {
            isLoadingFail = true
            isLoading = false
            animator.cancel()
            this.visibility = View.VISIBLE
            postInvalidate()
        }
    }
}
